/**
 * @file osal_log.c
 * @brief 
 * @author Tome (zigbee86@163.com)
 * @date 2021-01-05
 */
 



#include "osal_mod_cfg.h" 
 #include "hal_types.h"
 
#if  OS_MOD_LOG_ENABLE

#if ( OS_MOD_LOG_CHNL == 0 )
#include "SEGGER_RTT.h"
#else
#include "hal_uart.h"
#endif
 
 


#include "osal_Memory.h"

#include "hal_led.h"

#include "string.h"
#include "stdarg.h"
#include "stdio.h"
#include "osal_log.h"
#include "app_config.h"
 
/****************************************************************************************************************************** 
                                                            const
*******************************************************************************************************************************/












/****************************************************************************************************************************** 
                                                            typedef
*******************************************************************************************************************************/













/****************************************************************************************************************************** 
                                                            MICRO
*******************************************************************************************************************************/

#define LOG_PARAM_NULL        0X0
#define LOG_PARAM_LINE        0X1
#define LOG_PARAM_FUNC        0X2
#define LOG_PARAM_FILE        0X4 
#define LOG_PARAM_MEMALO      0x10
#define LOG_PARAM_TIME        0x20


#define LOG_COLOR_CODE_DEFAULT "\x1B[0m"
#define LOG_COLOR_CODE_BLACK   "\x1B[1;30m"
#define LOG_COLOR_CODE_RED     "\x1B[1;31m"
#define LOG_COLOR_CODE_GREEN   "\x1B[1;32m"
#define LOG_COLOR_CODE_YELLOW  "\x1B[1;33m"
#define LOG_COLOR_CODE_BLUE    "\x1B[1;34m"
#define LOG_COLOR_CODE_MAGENTA "\x1B[1;35m"
#define LOG_COLOR_CODE_CYAN    "\x1B[1;36m"
#define LOG_COLOR_CODE_WHITE   "\x1B[1;37m"


#define LOG_I_PARAM           ( 0 | LOG_PARAM_FUNC | LOG_PARAM_LINE | LOG_PARAM_MEMALO | LOG_PARAM_TIME ) 
#define LOG_W_PARAM           ( LOG_PARAM_FILE | LOG_PARAM_FUNC | LOG_PARAM_LINE | LOG_PARAM_MEMALO | LOG_PARAM_TIME ) 
#define LOG_E_PARAM           ( LOG_PARAM_FILE | LOG_PARAM_FUNC | LOG_PARAM_LINE | LOG_PARAM_MEMALO ) 


#define LOG_TYPE_INFO           0
#define LOG_TYPE_WARN           1
#define LOG_TYPE_ERROR          2



#define x_free      osal_mem_free
#define x_malloc    osal_mem_alloc


/****************************************************************************************************************************** 
                                                            declearation
*******************************************************************************************************************************/


static void osal_logo_and_ver(void);








/****************************************************************************************************************************** 
                                                            defination
*******************************************************************************************************************************/

static uint8 osal_log_buffer[OSAL_LOG_BUFFER_LENGTH];
static const uint8 log_paraable[3] = {LOG_I_PARAM, LOG_W_PARAM, LOG_E_PARAM};
static const char* log_prefix[3] = {
                                    LOG_COLOR_CODE_DEFAULT,
                                    LOG_COLOR_CODE_YELLOW,
                                    LOG_COLOR_CODE_RED,
                                    };


static uint8 g8_osal_log_register_taskid = 0xFF;

/*********************************************************************
 * @fn      osal_log_init
 *
 * @brief   
 *
 * @param   
 *
 * @return  
 */
void osal_log_init(void)
{
    #if ( OS_MOD_LOG_CHNL == 0 )
    SEGGER_RTT_Init();
    
    
    
    #else

	HalUartInit();
	
    #endif
    
    osal_logo_and_ver();
}



static void osal_logo_and_ver(void)
{
    LOG_RAW("----------------------------System Info----------------------------\r\n");
    LOG_RAW("OSAL ver: %s\r\n", OSAL_VERSION);
    LOG_RAW("FIRM ver: %s\r\n", FIRM_VERSION);
    LOG_RAW("PCB  ver: %s\r\n", PCB_VERSION);
    LOG_RAW("TLV  ver: %s\r\n", TLV_VERSION);
    LOG_RAW("MANU: %s\r\n", MANU_NAME);
    LOG_RAW("Complile: %s %s\r\n", __DATE__, __TIME__);
    //LOG_RAW("Copyright %s \r\n", "Newbit Studio");
    LOG_RAW("-------------------------------------------------------------------\r\n");
	
//	
}


void osal_log_print(void* p, uint32 len)
{
    #if ( OS_MOD_LOG_CHNL == 0 )
    (void)len;
    SEGGER_RTT_printf(0, p);
    
    #else
    
	HalUartPrintf(p);
    
    #endif
}

uint8 is_osal_log_printing(void)
{
    #if ( OS_MOD_LOG_CHNL == 0 )
    
    return 0;    
    
    #else
    
    return HalUartTxStaGet();
    #endif
}

uint8 osal_log_blocking(void)
{
    #if ( OS_MOD_LOG_CHNL == 0 )
    
    #else
    
    while(  HalUartTxStaGet() );
    
    #endif
    
    return 0;    
}


void osal_log_register(uint8 task_id)
{
    g8_osal_log_register_taskid = task_id;
}


#define LOG_MESSAGE_BUFFER_LENGTH   64
void osal_log_poll(void)
{
    uint16 size = 0;
    uint8  buf[LOG_MESSAGE_BUFFER_LENGTH + 1 ];
    logEvent_t* pMsg;
    
    #if ( OS_MOD_LOG_CHNL == 0 )
    //SEGGER_RTT_printf(0, p);
    size = SEGGER_RTT_Read(0, buf, LOG_MESSAGE_BUFFER_LENGTH);
    

    
    #else
    
	//HalUartPrintf(p);
    
    #endif
    
    if ( size  < 1 )return;
    if (size > LOG_MESSAGE_BUFFER_LENGTH ) return;
    
    buf[size] = 0;
    size++;
    
    

    pMsg = (logEvent_t*)osal_msg_allocate(sizeof(logEvent_t) + size);
    
    if ( pMsg == NULL ) return;
    
    pMsg->hdr.event = OSAL_LOG;
    // state
    pMsg->len = size;
    osal_memcpy(pMsg->data, buf,size);
    
    osal_msg_send( g8_osal_log_register_taskid, (uint8 *)pMsg );
}


/*********************************************************************
 * @fn      funcname
 *
 * @brief   
 *
 * @param   
 *
 * @param   
 *
 * @param   
 *
 * @return  
 */
void osal_log_out(uint8 type, const char* ptr_file,  const char *ptr_func, int line, uint8 newline, const char *fmt, ...)
{
    uint32 buf_len = 0;
    uint32 param = 0;
    uint8 name_len = 0;
    int str_len = 0;
    char *pmp = NULL, *pbuf = (char *)osal_log_buffer;
    char line_number[8];
    
    uint32 osal_ms = 0;
    uint32 osal_sec = 0;
    UTCTimeStruct utc;
    
    va_list ap;
    
    #define LOG_LENGTH_CHECK(n)          if ( (buf_len + n) >= OSAL_LOG_BUFFER_LENGTH)return;

    if(type >= 3)   
    {
        return;
    }        
    
    if (( LOG_TYPE_INFORMATIO_EN == 0) &&   ( type == LOG_TYPE_INFO )) return;
    if (( LOG_TYPE_WARNNING_EN == 0) &&     ( type == LOG_TYPE_WARN )) return;
    if (( LOG_TYPE_ERROR_EN == 0) &&        ( type == LOG_TYPE_ERROR )) return;
    
    memset(pbuf, 0, OSAL_LOG_BUFFER_LENGTH);
    param = log_paraable[type];

    if(type < 3)
    {
        strcpy(pbuf,log_prefix[type]);
        buf_len = strlen(log_prefix[type]);
        
        if ( param != 0 )
        {
            pbuf[buf_len++] = '<';
        }
        
        if(param & LOG_PARAM_TIME)
        {
            osal_ms = osal_GetTimeMs();
            osal_sec = osal_ms / 1000;
            osal_ms %= 1000;
            osal_ConvertUTCTime( &utc, osal_sec);
            str_len = sprintf(pbuf + buf_len, "%.2d:%.2d:%.2d.%.3d ", utc.hour,utc.minutes,utc.seconds,osal_ms);
            buf_len += str_len;
        }
        
        if(param & LOG_PARAM_FILE)
        {
            pmp = strrchr(ptr_file, '\\');
            pmp = pmp ? (pmp + 1) : (char*)ptr_file ;
            name_len = strlen(pmp);
            
            if(name_len > 20)
            {
                pmp = (char *)(pmp + name_len - 20);
                name_len = 20;
            }
            
            LOG_LENGTH_CHECK(name_len);
            
            memcpy(pbuf + buf_len, pmp, name_len);
            buf_len += name_len;
            pbuf[buf_len++] = ' ';
        }
        if(param & LOG_PARAM_FUNC)
        {
            pmp = (char *)ptr_func;
            name_len = strlen(ptr_func);
            LOG_LENGTH_CHECK(name_len);
            memcpy(pbuf + buf_len, pmp, name_len);
            buf_len += name_len;
            pbuf[buf_len++] = '(';
            pbuf[buf_len++] = ')';
            pbuf[buf_len++] = ' ';
        }
        if(param & LOG_PARAM_LINE)
        {
            str_len = sprintf(line_number, "L:%d", line);
            if(str_len < 0)
            {
                return;
            }
            LOG_LENGTH_CHECK(str_len);
            memcpy(pbuf + buf_len, line_number, str_len);
            buf_len += str_len;
            pbuf[buf_len++] = ' ';
        }
        
#if ( OSALMEM_METRICS )
        if ( param & LOG_PARAM_MEMALO )
        {//osal_heap_mem_used()
            str_len = sprintf(line_number, "M:%d", osal_heap_mem_used());
            #if 0
            if(str_len < 0)
            {
                return;
            }
            str_len = vsnprintf(pbuf + buf_len, OSAL_LOG_BUFFER_LENGTH - buf_len - 1, "M:%d",100 );
            #endif
            
            LOG_LENGTH_CHECK(str_len);
            memcpy(pbuf + buf_len, line_number, str_len);
            
            buf_len += str_len;
            pbuf[buf_len++] = ' ';
        }
        
#endif        
        
        if ( param != 0 )
        {
            pbuf[buf_len++] = '>';
            pbuf[buf_len++] = ' ';
        }
    }
    va_start(ap, fmt);
    str_len = vsnprintf(pbuf + buf_len, OSAL_LOG_BUFFER_LENGTH - buf_len - 1, fmt, ap);
    va_end(ap);
    if(str_len < 0)
    {
        return;
    }
    
    buf_len += str_len;
    
    if (newline)
    {
        pbuf[buf_len++] = '\r';
        pbuf[buf_len++] = '\n';
    }
    
    osal_log_print(pbuf, buf_len);
}


void util_hex2str( char *output, const uint8 *input, uint32 insize )
{
  const uint8 table[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
  uint32 i, c;
  
  if ( output == NULL )
    return;
  
  
  for ( i = 0; i < insize; i++ )
  {
     c = (*input & 0xf0) >> 4;
     *output++ = table[c];
    
     c = (*input & 0x0f );
     *output++ = table[c];
    
     input++;
  }
  
  *output = '\0';
  
}

/*
    HEX  DUMP PRINTF

*/

#if LOG_MOD_HEX_DUMP_ENABLE


#define STATIC_PRINTF_BUF_SIZE    128 
#define BUF_SIZE_MASK             0x7F  // 127


typedef struct _util_print_buf_
{
  uint32  len;
  uint32  idx;
  char      buf[STATIC_PRINTF_BUF_SIZE + 1];
  char      enter[3];   // also enter
  char      toolong[13];  // "log too long"
}util_print_buf_t;



static util_print_buf_t  util_print_buf ;

static bool _util_printf_initial = false;


static void _util_printf_buf_initial(void)
{
    util_print_buf.len = STATIC_PRINTF_BUF_SIZE;
    util_print_buf.idx = 0;
    memset(util_print_buf.buf, 0, STATIC_PRINTF_BUF_SIZE + 1 );
    //memset(util_print_buf.enter, 0, 2);
    util_print_buf.enter[0] = '\r';
    util_print_buf.enter[1] = '\n';
    util_print_buf.enter[2] = '\0';
    
    memcpy(util_print_buf.toolong, "log too long", sizeof("log too long"));
    _util_printf_initial = true;
}

void util_dump_hex_option(const uint8 *data, uint32 size, uint8 newline, uint8 block){
  char *str = NULL;
  uint32 hex_len = size*2 + 1;
  uint32 space0, space1;
    
  char *head1 = NULL, *head2 = NULL;
  
  if ( _util_printf_initial == false )
  {
    _util_printf_buf_initial();
  }
  
  
  str = x_malloc(  hex_len );
  
  if( str != NULL ){
    
    util_hex2str(str, data, size);
    
    space0 = util_print_buf.len - util_print_buf.idx;
    space1 = 0;
    
    if ( space0 < hex_len )
    {
      space1 = hex_len - space0;
    }
    else
    {
      space0 = hex_len;
    }
    
    head1 = util_print_buf.buf + util_print_buf.idx;
    memcpy(head1, str, space0);
    util_print_buf.idx += space0;
    
    if(space1)
    {
      head2 = util_print_buf.buf;
      memcpy(head2, str + space0, space1);
      util_print_buf.idx = space1;
    }
    
    util_print_buf.idx &= BUF_SIZE_MASK;
    
    if ( block )
    {
      //SEGGER_RTT_printf( 0, "%s", head1 );
      //if ( head2) SEGGER_RTT_printf( 0, "%s", head2 );
        LOG_RAW("%s", head1);
        if ( head2 )LOG_RAW("%s", head2);
      
    }
    else
    {
      LOG( "%s", head1 );
      if ( head2 )  LOG( "%s", head2 );
    }
    

    x_free( str );
  }
  
  if ( newline )
    LOG( "\n" );

}  

void util_dump_hex_option_log(const uint8 *data, uint32 size, char* log[2]){
  char *str = NULL;
  uint32 hex_len = size*2 + 1;
  uint32 space0, space1;
  
  char* head1 = NULL, *head2 = NULL;
  
  if ( _util_printf_initial == false )
  {
    _util_printf_buf_initial();
  }
  
  
  str = x_malloc(  hex_len );
  
  if( str != NULL ){
    
    util_hex2str(str, data, size);
    
    space0 = util_print_buf.len - util_print_buf.idx;
    space1 = 0;
    
    if ( space0 < hex_len )
    {
      space1 = hex_len - space0;
    }
    else
    {
      space0 = hex_len;
    }
    
    head1 = util_print_buf.buf + util_print_buf.idx;
    memcpy(head1, str, space0);
    util_print_buf.idx += space0;
    
    if(space1)
    {
      head2 = util_print_buf.buf;
      memcpy(head2, str + space0, space1);
      util_print_buf.idx = space1;
    }
    else
    {
      //head2 = util_print_buf.enter;
    }
    
    util_print_buf.idx &= BUF_SIZE_MASK;
    
    log[0] = head1;
    log[1] = head2;

    x_free( str );
  }
  else
  {
    log[0] = util_print_buf.toolong;
    log[1] = NULL;// util_print_buf.enter;
  }
}  

// print a string use util buffer
void util_string_log(const uint8 *data,uint32 size, char* log[2])
{
  char *str = (char*)data;
  uint32 str_len;
  char* head1 = NULL, *head2 = NULL;
    
  if ( size == 0 )
  {
    size = strlen(str);
  }
  
  str_len = size + 1;
  
  
  
  if ( _util_printf_initial == false )
  {
    _util_printf_buf_initial();
  }
  
  
  if ( size > STATIC_PRINTF_BUF_SIZE )
  {
    head1 = util_print_buf.toolong;;
    head2 = NULL;
  }
  else
  {
  
    uint32 space0 = util_print_buf.len - util_print_buf.idx;
    uint32 space1 = 0;
    
    if ( space0 < str_len )
    {
      space1 = str_len - space0;
    }
    else
    {
      space0 = str_len;
    }
    
    head1 = util_print_buf.buf + util_print_buf.idx;
    memcpy(head1, str, space0);
    util_print_buf.idx += space0;
    
    if(space1)
    {
      head2 = util_print_buf.buf;
      memcpy(head2, str + space0, space1);
      util_print_buf.idx = space1;
    }
    else
    {
      //head2 = util_print_buf.enter;
    }
    
    util_print_buf.idx &= BUF_SIZE_MASK;
  }

  log[0] = head1;
  log[1] = head2;
}


  
void util_dump_hex(const uint8 *data, uint32 size){
  bool newline = true;
  bool block = false;
  util_dump_hex_option(data, size, newline, block);
}


/*
 *  print hex string ,block MCU
*/
void util_dump_hex_block(const uint8 *data, uint32 size){
  
  bool newline = true;
  bool block = true;
    
  util_dump_hex_option(data, size, newline, block);
}


void util_dump_hex_log(const uint8 *data, uint32 size, char* log[2]){

  util_dump_hex_option_log(data, size, log);
}


#endif  // LOG_MOD_HEX_DUMP_ENABLE


char* get_enum_string(unsigned int val,unsigned int max, const enum_v_str_t* varstr)
{
	int i = 0;
  for (i = 0; (i != max) && (varstr[i].n < max); i++ ){if (varstr[i].n == val) return varstr[i].s;}
  return 0;
}



void HardFault_Handler(void)
{
    volatile int a = 0;

    while( 0 == a )
    {
        ;
    }
}

void _HardFault_Handler(uint32 * hardfault_args)
{
	#include "osal_log.h"
    volatile int a = 0;
	
	uint8 g_cDataBuf[128];
	const char* p = (const char*)g_cDataBuf;

	uint32 stacked_r0,stacked_r1,stacked_r2,stacked_r3;

	uint32 stacked_r12,stacked_lr, stacked_pc, stacked_psr;

	stacked_r0 = ((uint32) hardfault_args[0]);

	stacked_r1 = ((uint32) hardfault_args[1]);

	stacked_r2 = ((uint32) hardfault_args[2]);

	stacked_r3 = ((uint32) hardfault_args[3]);

	stacked_r12 = ((uint32) hardfault_args[4]);

	stacked_lr = ((uint32) hardfault_args[5]);

	stacked_pc = ((uint32) hardfault_args[6]);

	stacked_psr = ((uint32) hardfault_args[7]);
    
    

	sprintf((char*)g_cDataBuf,"[Hard fault handler]\n");

	LOG_RAW(p);

	sprintf((char*)g_cDataBuf,"SP = 0x%0.8x\n", (uint32)hardfault_args);

	LOG_RAW(p);

	sprintf((char*)g_cDataBuf,"R0 = 0x%0.8x\n", stacked_r0);

	LOG_RAW(p);

	sprintf((char*)g_cDataBuf,"R1 = 0x%0.8x\n", stacked_r1);

	LOG_RAW(p);

	sprintf((char*)g_cDataBuf,"R2 = 0x%0.8x\n", stacked_r2);

	LOG_RAW(p);

	sprintf((char*)g_cDataBuf,"R3 = 0x%0.8x\n", stacked_r3);

	LOG_RAW(p);

	sprintf((char*)g_cDataBuf,"R12 = 0x%0.8x\n", stacked_r12);

	LOG_RAW(p);

	sprintf((char*)g_cDataBuf,"LR = 0x%0.8x\n", stacked_lr);

	LOG_RAW(p);

	sprintf((char*)g_cDataBuf,"PC = 0x%0.8x\n", stacked_pc);

	LOG_RAW(p);

	sprintf((char*)g_cDataBuf,"PSR = 0x%0.8x\n", stacked_psr);

	LOG_RAW(p);

    
	
    while( 0 == a)
    {
        HalLedSet(HAL_LED_ALL, HAL_LED_MODE_ON);
        halMcuWaitMs(100);
        HalLedSet(HAL_LED_ALL, HAL_LED_MODE_OFF);
        halMcuWaitMs(100);
        ;
    }
}
    

#else

void HardFault_Handler(void)
{
    volatile int a = 0;

    while( 0 == a )
    {
        ;
    }
}


void _HardFault_Handler(uint32 * hardfault_args)
{
    HardFault_Handler();
}

void osal_log_register(uint8 task_id){};



#endif // file


